package lumis.portal.report;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Transient;

import lumis.portal.PortalException;
import lumis.portal.event.persistence.BasePortalEntity;
import lumis.portal.stability.StableMinor;
import lumis.util.XmlUtil;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Type;
import org.w3c.dom.Document;
import org.w3c.dom.Node;

/**
 * The type of a {@link Report}. Holds configuration information for 
 * reports of this type.
 * 
 * @stereotype entity
 * @composed 1 - * ReportTypeParameter
 * 
 * @since 4.2.0
 * @version $Revision: 13082 $ $Date: 2011-05-27 16:03:12 -0300 (Fri, 27 May 2011) $
 */
@StableMinor(version = "6.0", sinceVersion = "4.2")
@Entity
@Table(name="lum_RepType")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
public class ReportType extends BasePortalEntity
{
	@Id
	private String id;
	
	private String reportPath;
	
	@Type(type="text")
	@Column(name="configParametersDef")
	private String configParametersDefinition;
	
	@Type(type="text")
	@Column(name="runtimeParametersDef")
	private String runtimeParametersDefinition;
	
	/**
	 * Stores the value of {@link #configParametersDefinition} converted to 
	 * a XML node.
	 * @since 4.2.0 
	 */
	@Transient
	private Node configParametersDefinitionNode;
	
	/**
	 * Stores the value of {@link #runtimeParametersDefinition} converted to 
	 * a XML node.
	 * @since 4.2.0 
	 */
	@Transient
	private Node runtimeParametersDefinitionNode;
	
	private String baseUrl;
	
	private String dataSourceProvider;
	
	@OneToMany(mappedBy="reportType", cascade=CascadeType.ALL)
	@Cache(usage=CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
	private Collection<ReportTypeParameter> parameters = new ArrayList<ReportTypeParameter>();
	
	// -- Property Methods -- //
	
	/**
	 * Returns this report type identifier.
	 * @return the report type identifier. Example: <em>lumis.report.pageview</em>.
	 * @since 4.2.0
	 */
	public String getId()
	{
		return id;
	}

	/**
	 * Sets this report type identifier. Should not be used to change already 
	 * existing report types, only for setting the identifier for new types.
	 * @param id the identifier. Example: <em>lumis.report.pageview</em>.
	 * @since 4.2.0
	 */
	public void setId(String id)
	{
		this.id = id;
	}
	
	/**
	 * Returns the url prefix for obtaining this report type's strings.
	 * @return the strings url prefix for this report type.
	 * @since 4.2.0
	 */
	public String getStringsUrlPrefix()
	{
		return getBaseUrl()+"/strings/strings";
	}
	
	/**
	 * Returns the path for this report type, relative to lumisdata/def directory.
	 * @return the path for this report type, relative to lumisdata/def directory.
	 * @since 4.2.0
	 */
	public String getReportPath()
	{
		return reportPath;
	}

	/**
	 * Sets the path for this report type, relative to lumisdata/def directory.
	 * @param reportPath the path.
	 * @since 4.2.0
	 */
	public void setReportPath(String reportPath)
	{
		this.reportPath = reportPath;
	}

	/**
	 * Returns the registration base url for this report type. It is relative
	 * to lumisdata/def directory. Example: <em>lumis/report/pageview</em>
	 * @return the base url.
	 * @since 4.2.0
	 */
	public String getBaseUrl()
	{
		return baseUrl;
	}

	/**
	 * Sets the registration base url for this report type.
	 * @param baseUrl the base url.
	 * @see #getBaseUrl()
	 * @since 4.2.0
	 */
	public void setBaseUrl(String baseUrl)
	{
		this.baseUrl = baseUrl;
	}
	
	/**
	 * Returns the data source provider class name for reports of this type.
	 * @return the data source provider class name, or null if no custom
	 * data source provider was specified.
	 * @since 4.2.0
	 */
	public String getDataSourceProvider()
	{
		return dataSourceProvider;
	}

	/**
	 * Sets the data source provider for reports of this type.
	 * @param dataSourceProvider the data source provider class name.
	 * @see #getDataSourceProvider()
	 * @since 4.2.0
	 */
	public void setDataSourceProvider(String dataSourceProvider)
	{
		this.dataSourceProvider = dataSourceProvider;
	}

	/**
	 * Sets the configuration parameters definition for this report type.
	 * @param configParametersDefinitionNode the configuration parameters 
	 * definition node.
	 * @since 4.2.0
	 */
	public void setConfigParametersDefinitionNode(Node configParametersDefinitionNode)
	{
		this.configParametersDefinitionNode = configParametersDefinitionNode;
		if (configParametersDefinitionNode != null)
		{
			try
			{
				this.configParametersDefinition = XmlUtil.getXmlString(configParametersDefinitionNode);
			}
			catch (PortalException e)
			{
				throw new IllegalArgumentException("Could not convert the given node to string", e);
			}
		}
		else
		{
			this.configParametersDefinition = null;
		}
	}
	
	/**
	 * Sets the runtime parameters definition for this report type.
	 * @param runtimeParametersDefinitionNode the runtime parameters 
	 * definition node.
	 * @since 4.2.0
	 */
	public void setRuntimeParametersDefinitionNode(Node runtimeParametersDefinitionNode)
	{
		this.runtimeParametersDefinitionNode = runtimeParametersDefinitionNode;
		if (runtimeParametersDefinitionNode != null)
		{
			try
			{
				this.runtimeParametersDefinition = XmlUtil.getXmlString(runtimeParametersDefinitionNode);
			}
			catch (PortalException e)
			{
				throw new IllegalArgumentException("Could not convert the given node to string", e);
			}
		}
		else
		{
			this.runtimeParametersDefinition = null;
		}
	}
	
	/**
	 * Returns an immutable view of the parameter of this report type.
	 * @return an immutable collection containing the parameters of this report 
	 * type.
	 * @since 4.2.0
	 */
	public Collection<ReportTypeParameter> getParameters()
	{
		return Collections.unmodifiableCollection(parameters);
	}
	
	/**
	 * Adds a parameter in this report type.
	 * @return the parameter added.
	 * @since 4.2.0
	 */
	public ReportTypeParameter addParameter()
	{
		ReportTypeParameter parameter = new ReportTypeParameter();
		parameters.add(parameter);
		parameter.setReportType(this);
		return parameter;
	}
	
	// -- Operations -- //
	
	/**
	 * Removes a parameter from this report type.
	 * @param reportTypeParameter the parameter.
	 * @since 4.2.0
	 */
	void removeParameter(ReportTypeParameter reportTypeParameter)
	{
		parameters.remove(reportTypeParameter);
	}
	
	/**
	 * Returns the controls node for the configuration parameters.
	 * <p>
	 * If no configuration parameters controls node was defined for this report type, 
	 * a default one is generated and returned by this method.
	 * @return the configuration parameters controls node, or null if none
	 * was defined and a default could not be generated based on this
	 * report type.
	 * @since 4.2.0
	 */
	public Node getConfigParametersControlsNode() throws PortalException
	{
		if (this.configParametersDefinitionNode != null)
		{
			Node controlsNode = XmlUtil.selectSingleNode("controls", this.configParametersDefinitionNode);
			if(controlsNode != null)
			{
				return controlsNode;
			}
			else
			{
				Node sourceNode = XmlUtil.selectSingleNode("source", this.configParametersDefinitionNode);
				String sourceId = "configSource";
				if(sourceNode != null)
					sourceId = XmlUtil.readAttributeString("id", sourceNode, sourceId);
				
				String controls = XmlUtil.XML_HEADER+"<controls><control type=\"lum_autoLayoutFields\" sourceId=\""+sourceId+"\"/></controls>";
				return XmlUtil.getDocument(controls).getDocumentElement();
			}
		}
		else
		{
			return null;
		}
	}

	/**
	 * Returns the configuration parameters source node.
	 * @return the configuration parameters source node or null if none was defined.
	 * @since 4.2.0
	 */
	public Node getConfigParametersSourceNode() throws PortalException
	{
		if (this.configParametersDefinitionNode != null)
			return XmlUtil.selectSingleNode("source", this.configParametersDefinitionNode);
		else
			return null;
	}
	
	/**
	 * Returns the controls node for the runtime parameters.
	 * <p>
	 * If no runtime parameters controls node was defined for this report type, 
	 * a default one is generated and returned by this method.
	 * @return the runtime parameters controls node, or null if none
	 * was defined and a default could not be generated based on this
	 * report type.
	 * @since 4.2.0
	 */
	public Node getRuntimeParametersControlsNode() throws PortalException
	{
		if (this.runtimeParametersDefinitionNode != null)
		{
			Node controlsNode = XmlUtil.selectSingleNode("controls", this.runtimeParametersDefinitionNode);
			
			if(controlsNode != null)
			{
				return controlsNode;
			}
			else
			{
				Node sourceNode = XmlUtil.selectSingleNode("source", this.runtimeParametersDefinitionNode);
				String sourceId = "runtimeSource";
				if(sourceNode != null)
					sourceId = XmlUtil.readAttributeString("id", sourceNode, sourceId);
				
				String controls = XmlUtil.XML_HEADER+"<controls><control type=\"lum_autoLayoutFields\" sourceId=\""+sourceId+"\"/></controls>";
				return XmlUtil.getDocument(controls).getDocumentElement();
			}
		}
		else
		{
			return null;
		}
	}

	/**
	 * Returns the runtime parameters source node.
	 * @return the runtime parameters source node or null if none was defined.
	 * @since 4.2.0
	 */
	public Node getRuntimeParametersSourceNode() throws PortalException
	{
		if (this.runtimeParametersDefinitionNode != null)
		{
			return XmlUtil.selectSingleNode("source", this.runtimeParametersDefinitionNode);
		}
		
		return null;
	}
	
	// -- Events -- //

	@Override
	protected void onPostLoad() throws Exception
	{
		// calculate nodes from strings read from the database
		if (configParametersDefinition != null)
		{
			Document configParametersDoc = XmlUtil.getXMLDocumentFromString(configParametersDefinition);
			configParametersDefinitionNode = configParametersDoc.getDocumentElement();			
		}
		if (runtimeParametersDefinition != null)
		{
			Document runtimeParametersDoc = XmlUtil.getXMLDocumentFromString(runtimeParametersDefinition);
			runtimeParametersDefinitionNode = runtimeParametersDoc.getDocumentElement();			
		}
		
		// send the post load portal event
		super.onPostLoad();
	}
}